package com.yuzhihao.writejvm.read;

import com.yuzhihao.writejvm.format.PrintFormat;
import lombok.Data;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * 处理访问标识
 *
 * @author yzh
 * @since 2021年12月09日19:03:10
 */
public class TypeFatory {

    private final static int[] ACCESS_VALUES = {16384, 8192, 4096, 1024, 512, 128, 64, 32, 16, 8, 4, 2, 1};

    private final static Map<Integer, Access> ACCESS_DESC = new HashMap<>();

    private final static Map<String, String> FIELD_TYPE = new HashMap<>();

    static {
        //访问标识符
        ACCESS_DESC.put(32768, new Access(32768, "mandated", "ACC_MANDATED"));
        ACCESS_DESC.put(16384, new Access(16384, "enum", "ACC_ENUM"));
        ACCESS_DESC.put(8192, new Access(8192, "annotation", "ACC_ANNOTATION"));
        ACCESS_DESC.put(4096, new Access(4096, "synchronized", "ACC_SYNTHETIC"));
        ACCESS_DESC.put(1024, new Access(1024, "abstract", "ACC_ABSTRACT"));
        ACCESS_DESC.put(512, new Access(512, "interface", "ACC_INTERFACE"));
        ACCESS_DESC.put(128, new Access(128, "transient", "ACC_TRANSIENT"));
        ACCESS_DESC.put(64, new Access(64, "volatile", "ACC_VOLATILE"));
        ACCESS_DESC.put(32, new Access(32, "super", "ACC_SUPER"));
        ACCESS_DESC.put(16, new Access(16, "final", "ACC_FINAL"));
        ACCESS_DESC.put(8, new Access(8, "static", "ACC_STATIC"));
        ACCESS_DESC.put(4, new Access(4, "protected", "ACC_PROTECTED"));
        ACCESS_DESC.put(2, new Access(2, "private", "ACC_PRIVATE"));
        ACCESS_DESC.put(1, new Access(1, "public", "ACC_PUBLIC"));

        //字段类型
        FIELD_TYPE.put("B", "byte");
        FIELD_TYPE.put("C", "char");
        FIELD_TYPE.put("D", "double");
        FIELD_TYPE.put("F", "float");
        FIELD_TYPE.put("I", "int");
        FIELD_TYPE.put("J", "long");
        FIELD_TYPE.put("L", "reference");
        FIELD_TYPE.put("S", "short");
        FIELD_TYPE.put("Z", "boolean");
        FIELD_TYPE.put("[", "reference");
        FIELD_TYPE.put("V", "void");
    }

    /**
     * 通过描述符解析方法参数
     * @param term
     * @return
     * @since <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-VoidDescriptor">jvm</a>
     */
    public static String parse_parameters(String term) {
        StringBuilder builder = new StringBuilder();
        term = term.substring(1);
        while (true) {
            String index = term.substring(0, 1);
            if (")".equals(index)) {
                break;
            }
            term = term.substring(1);
            if ("[".equals(index)) {
                String next = term.substring(0, 1);
                term = term.substring(1);
                if ("L".equals(next)) {
                    int i = term.indexOf(";");
                    builder.append(term, 0, i).append(",").append(PrintFormat.EMPTY);
                    term = term.substring(i + 1);
                } else {
                    builder.append(TypeFatory.field_type(next)).append(",").append(PrintFormat.EMPTY);
                }
            } else if ("L".equals(index)) {
                int i = term.indexOf(";");
                builder.append(term, 0, i).append(",").append(PrintFormat.EMPTY);
                term = term.substring(i + 1);
            } else {
                builder.append(TypeFatory.field_type(index)).append(",").append(PrintFormat.EMPTY);
            }
        }
        if (builder.length() > 0) {
            int i = builder.lastIndexOf(",");
            builder.delete(i,builder.length());
        }

        return builder.toString();
    }

    public static String field_type(String term) {
        if (term.contains("[L")) {
            return term.substring(2, term.length() - 1) + "[]";
        } else if (term.contains("[")) {
            return FIELD_TYPE.get(term.substring(1, 2)) + "[]";
        }
        if (term.contains("L")) {
            return term.substring(1, term.length() - 1);
        }
        return FIELD_TYPE.get(term);
    }

    /**
     * 解析描述符 "()V" ？ V
     *
     * @param desc
     * @return
     */
    public static String parse_desc(String desc) {
        int i = desc.lastIndexOf(")");
        if (i != -1) {
            return desc.substring(i + 1);
        }
        return desc;
    }

    public static List<Access> access_flags(int value) {
        List<Access> flags = new ArrayList<>();
        int h = value;
        while (h > 0) {
            for (int i : ACCESS_VALUES) {
                if (h >= i) {
                    flags.add(ACCESS_DESC.get(i));
                    h = h - i;
                }
            }
        }
        flags.sort(Comparator.comparingInt(Access::getB));
        return flags;
    }

    public static Access access_flag(int value) {
        return ACCESS_DESC.get(value);
    }

    @Data
    public static class Access {
        private int b;

        private String name;

        private String fname;

        public Access(int b, String name, String fname) {
            this.b = b;
            this.name = name;
            this.fname = fname;
        }
    }

}
